package msgs

import (
	"encoding/binary"
	"fmt"
	"log"
	"ospf/common"
	"ospf/tables"
)

const (
	// no trailing array of neighbor IDS, namely the shortest hello package length.
	helloLen = 20
)

// Hello implements Packet
type Hello struct {
	Header                   Header
	NetworkMask              common.ID
	HelloInterval            uint16
	Options                  Options
	RouterPriority           uint8
	RouterDeadInterval       uint32
	DesignatedRouterID       common.ID
	BackupDesignatedRouterID common.ID
	NeighborIDs              []common.ID
}

func (h *Hello) GetHeader() *Header {
	return &h.Header
}

func (h *Hello) Len() int {
	//TODO implement me
	panic("implement me")
}

func (h *Hello) Serialize() ([]byte, error) {
	if !h.Options.Valid() {
		return nil, common.ErrSerialize("header options invalid.")
	}
	data := make([]byte, headerLen+helloLen+len(h.NeighborIDs)*4)
	err := h.Header.Serialize(data[:headerLen])
	if err != nil {
		return nil, common.ErrSerialize("header serialize failed.")
	}
	copy(data[headerLen:headerLen+4], h.NetworkMask[:])
	binary.BigEndian.PutUint16(data[headerLen+4:headerLen+6], h.HelloInterval)
	data[headerLen+6] = byte(h.Options)
	data[headerLen+7] = h.RouterPriority
	binary.BigEndian.PutUint32(data[headerLen+8:headerLen+12], h.RouterDeadInterval)
	copy(data[headerLen+12:headerLen+16], h.DesignatedRouterID[:])
	copy(data[headerLen+16:headerLen+20], h.BackupDesignatedRouterID[:])
	for i, id := range h.NeighborIDs {
		copy(data[headerLen+20+i*4:headerLen+24+i*4], id[:])
	}
	return data, nil
}

func (h *Hello) Deserialize(data []byte) error {
	// pre check
	if l := len(data); l < helloLen {
		return fmt.Errorf("not enough bytes for Hello packet: %d < %d", l, helloLen)
	}
	if l := len(data); l%4 != 0 {
		return fmt.Errorf("hello package not complete, size: %d", l)
	}

	// map
	copy(h.NetworkMask[:], data[0:4])
	h.HelloInterval = binary.BigEndian.Uint16(data[4:6])
	h.Options = Options(data[6])
	h.RouterPriority = data[7]
	h.RouterDeadInterval = binary.BigEndian.Uint32(data[8:12])
	copy(h.DesignatedRouterID[:], data[12:16])
	copy(h.BackupDesignatedRouterID[:], data[16:20])
	for i := 20; i < len(data); i += 4 {
		h.NeighborIDs = append(h.NeighborIDs, common.ID(data[i:i+4]))
	}
	return nil
}

func GenHello(IP common.ID, lsDB *tables.OspfLSDB) *Hello {
	h := &Hello{
		Header:                   *GenHeader(HelloType, lsDB.RouterID),
		NetworkMask:              common.ID{255, 255, 255, 0},
		HelloInterval:            10,
		Options:                  DefaultHelloOption(),
		RouterPriority:           1,
		RouterDeadInterval:       40,
		DesignatedRouterID:       lsDB.RouterID,
		BackupDesignatedRouterID: IP,
		NeighborIDs:              lsDB.NeighT.GetNeighborsSimple(),
	}
	h.Header.PacketLength += helloLen + uint16(4*len(h.NeighborIDs))
	data, err := SerializePacket(h)
	if err != nil {
		log.Fatal(common.ErrSerialize("fail to calculate hello checksum."))
		return nil
	}
	h.Header.CheckSum = CalculateCheckSum(data)
	return h
}
